package com.binance.mbxgateway.web.service.impl;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import com.binance.mbxgateway.web.service.IMatchboxService;
import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class MatchboxServiceImpl implements IMatchboxService {

    @Autowired
    private RestTemplate restTemplate;
    @Value("${matchbox.management.root.url}")
    private String matchboxUrl;

    @Override
    public String postOrder(@NotEmpty Object accountId, @NotEmpty Object quantity, @NotEmpty Object side,
            @NotEmpty Object symbol, @NotEmpty Object type, Object force, Object icebergQty, Object newClientOrderId,
            Object newOrderRespType, Object price, Object stopPrice, Object targetOrderId, Object timeInForce) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/order", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbol", symbol).queryParam("side", side)
                .queryParam("type", type).queryParam("timeInForce", timeInForce).queryParam("price", price)
                .queryParam("quantity", quantity).queryParam("newClientOrderId", newClientOrderId)
                .queryParam("stopPrice", stopPrice).queryParam("icebergQty", icebergQty)
                .queryParam("newOrderRespType", newOrderRespType).queryParam("targetOrderId", targetOrderId)
                .queryParam("force", force).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String getOrder(@NotEmpty Object symbol, Object orderId, Object origClientOrderId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/order", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("orderId", orderId)
                .queryParam("origClientOrderId", origClientOrderId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getOpenOrders(@NotEmpty Object accountId, Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/openOrders", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAllOrders(@NotEmpty Object accountId, Object endTime, Object fromId, Object limit,
            Object startTime, Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/allOrders", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbol", symbol).queryParam("startTime", startTime)
                .queryParam("endTime", endTime).queryParam("fromId", fromId).queryParam("limit", limit).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String deleteOrder(@NotEmpty Object accountId, @NotEmpty Object symbol, Object force,
            Object newClientOrderId, Object orderId, Object origClientOrderId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/order", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbol", symbol).queryParam("orderId", orderId)
                .queryParam("origClientOrderId", origClientOrderId).queryParam("newClientOrderId", newClientOrderId)
                .queryParam("force", force).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String deleteOpenOrders(@NotEmpty Object accountId, @NotEmpty Object symbol, Object force) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/openOrders", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbol", symbol).queryParam("force", force).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String getSymbolOrders(@NotEmpty Object symbol, Object endTime, Object fromId, Object limit,
            Object startTime) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbolOrders", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("startTime", startTime).queryParam("endTime", endTime)
                .queryParam("fromId", fromId).queryParam("limit", limit).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String postAccount(@NotEmpty Object externalId, Object buyerCommission, Object canDeposit, Object canTrade,
            Object canWithdraw, Object makerCommission, Object sellerCommission, Object takerCommission) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/account", matchboxUrl))
                .queryParam("makerCommission", makerCommission).queryParam("takerCommission", takerCommission)
                .queryParam("buyerCommission", buyerCommission).queryParam("sellerCommission", sellerCommission)
                .queryParam("externalId", externalId).queryParam("canTrade", canTrade)
                .queryParam("canWithdraw", canWithdraw).queryParam("canDeposit", canDeposit).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String getAccount(@NotEmpty Object accountId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/account", matchboxUrl))
                .queryParam("accountId", accountId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String putAccount(@NotEmpty Object accountId, @NotEmpty Object canDeposit, @NotEmpty Object canTrade,
            @NotEmpty Object canWithdraw) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/account", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("canTrade", canTrade)
                .queryParam("canWithdraw", canWithdraw).queryParam("canDeposit", canDeposit).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String putAccountCommission(@NotEmpty Object accountId, @NotEmpty Object buyerCommission,
            @NotEmpty Object makerCommission, @NotEmpty Object sellerCommission, @NotEmpty Object takerCommission) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/account/commission", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("makerCommission", makerCommission)
                .queryParam("takerCommission", takerCommission).queryParam("buyerCommission", buyerCommission)
                .queryParam("sellerCommission", sellerCommission).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String postBalance(@NotEmpty Object accountId, @NotEmpty Object asset, @NotEmpty Object balanceDelta,
            @NotEmpty Object updateId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/balance", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("asset", asset).queryParam("updateId", updateId)
                .queryParam("balanceDelta", balanceDelta).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String getBalanceLedger(@NotEmpty Object accountId, @NotEmpty Object asset, @NotEmpty Object endTime,
            @NotEmpty Object startTime) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/balanceLedger", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("asset", asset).queryParam("startTime", startTime)
                .queryParam("endTime", endTime).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAllAccounts() {
        String url =
                UriComponentsBuilder.fromHttpUrl(String.format("%s/allAccounts", matchboxUrl)).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAccountByExternalId(@NotEmpty Object externalId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/accountByExternalId", matchboxUrl))
                .queryParam("externalId", externalId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String putAccountSymbolsCommission(@NotEmpty Object accountId, @NotEmpty Object buyerCommission,
            @NotEmpty Object makerCommission, @NotEmpty Object sellerCommission, @NotEmpty Object symbols,
            @NotEmpty Object takerCommission) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/accountSymbolsCommission", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbols", symbols)
                .queryParam("makerCommission", makerCommission).queryParam("takerCommission", takerCommission)
                .queryParam("buyerCommission", buyerCommission).queryParam("sellerCommission", sellerCommission).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String getAccountSymbolsCommission(@NotEmpty Object accountId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/accountSymbolsCommission", matchboxUrl))
                .queryParam("accountId", accountId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAccountSymbolCommissionHistory(@NotEmpty Object accountId, @NotEmpty Object endTime,
            @NotEmpty Object startTime, @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/accountSymbolCommissionHistory", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbol", symbol).queryParam("startTime", startTime)
                .queryParam("endTime", endTime).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAccountTPlusLockState(@NotEmpty Object accountId, @NotEmpty Object symbols) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/accountTPlusLockState", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbols", symbols).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String putGasOptOut(@NotEmpty Object accountId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/gasOptOut", matchboxUrl))
                .queryParam("accountId", accountId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String deleteGasOptOut(@NotEmpty Object accountId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/gasOptOut", matchboxUrl))
                .queryParam("accountId", accountId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String putAccountAsset(@NotEmpty Object accountId, @NotEmpty Object asset, @NotEmpty Object available,
            @NotEmpty Object locked, @NotEmpty Object symbolType) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/account/asset", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("asset", asset).queryParam("symbolType", symbolType)
                .queryParam("locked", locked).queryParam("available", available).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String getOrderRateLimit(@NotEmpty Object rateInterval) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/orderRateLimit", matchboxUrl))
                .queryParam("rateInterval", rateInterval).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String putOrderRateLimit(@NotEmpty Object limit, @NotEmpty Object rateInterval) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/orderRateLimit", matchboxUrl))
                .queryParam("rateInterval", rateInterval).queryParam("limit", limit).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String putOrderRateLimitAccount(@NotEmpty Object accountId, @NotEmpty Object limit,
            @NotEmpty Object rateInterval) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/orderRateLimit/account", matchboxUrl))
                .queryParam("rateInterval", rateInterval).queryParam("accountId", accountId).queryParam("limit", limit)
                .build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String deleteOrderRateLimitAccount(@NotEmpty Object accountId, @NotEmpty Object rateInterval) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/orderRateLimit/account", matchboxUrl))
                .queryParam("rateInterval", rateInterval).queryParam("accountId", accountId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String getApiKeys(@NotEmpty Object accountId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKeys", matchboxUrl))
                .queryParam("accountId", accountId).toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String postApiKey(@NotEmpty Object accountId, @NotEmpty Object desc, Object canAccessSecureWs,
            Object canControlUserStreams, Object canTrade, Object canViewMarketData, Object canViewUserData,
            Object force) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("desc", desc).queryParam("canTrade", canTrade)
                .queryParam("canViewUserData", canViewUserData)
                .queryParam("canControlUserStreams", canControlUserStreams)
                .queryParam("canViewMarketData", canViewMarketData).queryParam("canAccessSecureWs", canAccessSecureWs)
                .queryParam("force", force).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    private static String formatUrl(String url) {
        StringBuilder sb = new StringBuilder();
        String[] array = StringUtils.split(url, "?");
        sb.append(array[0]);
        if (array.length > 1) {
            array = StringUtils.split(array[1], "&");
            if (array != null && array.length > 0) {
                List<String> params = Lists.newArrayList(array);
                for (Iterator<String> iter = params.iterator(); iter.hasNext();) {
                    String p = iter.next();
                    if (!StringUtils.contains(p, "=")) {
                        iter.remove();
                    }
                }
                if (!params.isEmpty()) {
                    sb.append("?");
                    sb.append(StringUtils.join(params, "&"));
                }
            }
        }
        url = sb.toString();
        log.info("Will Send request to matchbox,the url is {}", url);
        return url;
    }

    @Override
    public String deleteApiKey(@NotEmpty Object accountId, @NotEmpty Object keyId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey", matchboxUrl))
                .queryParam("keyId", keyId).queryParam("accountId", accountId).toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postApiKeyRule(@NotEmpty Object accountId, @NotEmpty Object ip, @NotEmpty Object keyId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey/rule", matchboxUrl))
                .queryParam("keyId", keyId).queryParam("accountId", accountId).queryParam("ip", ip).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteApiKeyRule(@NotEmpty Object accountId, @NotEmpty Object keyId, @NotEmpty Object ruleId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey/rule", matchboxUrl))
                .queryParam("ruleId", ruleId).queryParam("keyId", keyId).queryParam("accountId", accountId).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String getApiKeyCheck(@NotEmpty Object apiKey, @NotEmpty Object ip, Object payload, Object recvWindow,
            Object signature, Object timestamp) {
        String payloadStr = payload == null ? null : payload.toString();
        if (StringUtils.isNotBlank(payloadStr)) {
            try {
                payloadStr = URLDecoder.decode(payloadStr, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.error("getApiKeyCheck.URLDecoder.decode occurs error:{},{}", payload, e);
            }
        }
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey/check", matchboxUrl))
                .queryParam("ip", ip).queryParam("payload", payloadStr).queryParam("recvWindow", recvWindow)
                .queryParam("timestamp", timestamp).queryParam("signature", signature).toUriString();
        url = formatUrl(url);
        HttpClient httpClient = new DefaultHttpClient();
        // get method
        HttpGet httpGet = new HttpGet(url);
        // set header
        httpGet.setHeader("X-MBX-APIKEY", apiKey.toString());
        // response
        HttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
        } catch (Exception e) {
            log.error("getApiKeyCheck验证签名出错1：" + e);
            throw new RuntimeException(e);
        }
        // get response into String
        String json = "";
        try {
            org.apache.http.HttpEntity entity = response.getEntity();
            json = EntityUtils.toString(entity, "UTF-8");
            log.info("getApiKeyCheck验证签名结果：{}",json);
        } catch (Exception e) {
            log.error("getApiKeyCheck验证签名出错2：" + e);
            throw new RuntimeException(e);
        }
        return json;
    }

    @Override
    public String putApiKeyPermissions(@NotEmpty Object accountId, @NotEmpty Object canAccessSecureWs,
            @NotEmpty Object canControlUserStreams, @NotEmpty Object canTrade, @NotEmpty Object canViewMarketData,
            @NotEmpty Object canViewUserData, @NotEmpty Object keyId, Object force) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey/permissions", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("keyId", keyId).queryParam("canTrade", canTrade)
                .queryParam("canViewUserData", canViewUserData)
                .queryParam("canControlUserStreams", canControlUserStreams)
                .queryParam("canViewMarketData", canViewMarketData).queryParam("canAccessSecureWs", canAccessSecureWs)
                .queryParam("force", force).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String getApiKeyLock(@NotEmpty Object accountId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey/lock", matchboxUrl))
                .queryParam("accountId", accountId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String putApiKeyLock(@NotEmpty Object accountId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey/lock", matchboxUrl))
                .queryParam("accountId", accountId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String deleteApiKeyLock(@NotEmpty Object accountId, @NotEmpty Object revert) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/apiKey/lock", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("revert", revert).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String getEstimateSymbol(@NotEmpty Object baseDecimalPlaces, @NotEmpty Object quoteDecimalPlaces,
            Object mathSystemType, Object maxPrice, Object maxQty) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/estimateSymbol", matchboxUrl))
                .queryParam("baseDecimalPlaces", baseDecimalPlaces).queryParam("quoteDecimalPlaces", quoteDecimalPlaces)
                .queryParam("maxPrice", maxPrice).queryParam("maxQty", maxQty)
                .queryParam("mathSystemType", mathSystemType).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAssets() {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/assets", matchboxUrl)).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String postAsset(@NotEmpty Object asset, @NotEmpty Object decimalPlaces) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/asset", matchboxUrl)).queryParam("asset", asset)
                .queryParam("decimalPlaces", decimalPlaces).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String getSymbols() {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbols", matchboxUrl)).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String postSymbol(@NotEmpty Object baseAsset, @NotEmpty Object commissionType, @NotEmpty Object maxPrice,
            @NotEmpty Object maxQty, @NotEmpty Object minQty, @NotEmpty Object quoteAsset, @NotEmpty Object symbol,
            @NotEmpty Object symbolType, Object matchingUnitType, Object mathSystemType) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbol", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("symbolType", symbolType).queryParam("baseAsset", baseAsset)
                .queryParam("quoteAsset", quoteAsset).queryParam("minQty", minQty).queryParam("maxQty", maxQty)
                .queryParam("maxPrice", maxPrice).queryParam("commissionType", commissionType)
                .queryParam("matchingUnitType", matchingUnitType).queryParam("mathSystemType", mathSystemType).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String putSymbol(@NotEmpty Object symbol, @NotEmpty Object symbolStatus) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbol", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("symbolStatus", symbolStatus).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String getSymbolHistory(@NotEmpty Object startTime, @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbolHistory", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("startTime", startTime).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAuctionReports(@NotEmpty Object startTime, @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/auctionReports", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("startTime", startTime).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String putOrderTypes(@NotEmpty Object enableIceberg, @NotEmpty Object enableMarket,
            @NotEmpty Object enableStopLoss, @NotEmpty Object enableStopLossLimit, @NotEmpty Object enableTakeProfit,
            @NotEmpty Object enableTakeProfitLimit, @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/orderTypes", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("enableMarket", enableMarket)
                .queryParam("enableStopLoss", enableStopLoss).queryParam("enableStopLossLimit", enableStopLossLimit)
                .queryParam("enableIceberg", enableIceberg).queryParam("enableTakeProfit", enableTakeProfit)
                .queryParam("enableTakeProfitLimit", enableTakeProfitLimit).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String putSymbolCommission(@NotEmpty Object buyerCommission, @NotEmpty Object makerCommission,
            @NotEmpty Object sellerCommission, @NotEmpty Object symbol, @NotEmpty Object takerCommission) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbolCommission", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("makerCommission", makerCommission)
                .queryParam("takerCommission", takerCommission).queryParam("buyerCommission", buyerCommission)
                .queryParam("sellerCommission", sellerCommission).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String getSymbolCommission(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbolCommission", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getSymbolCommissionHistory(@NotEmpty Object endTime, @NotEmpty Object startTime,
            @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbolCommissionHistory", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("startTime", startTime).queryParam("endTime", endTime).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String putSymbolCommissionType(@NotEmpty Object commissionType, @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/symbolCommissionType", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("commissionType", commissionType).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String getUserTrades(@NotEmpty Object accountId, Object endTime, Object fromId, Object limit,
            Object startTime, Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/userTrades", matchboxUrl))
                .queryParam("accountId", accountId).queryParam("symbol", symbol).queryParam("startTime", startTime)
                .queryParam("endTime", endTime).queryParam("fromId", fromId).queryParam("limit", limit).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getTrades(@NotEmpty Object symbol, Object endTime, Object fromId, Object limit, Object startTime) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/trades", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("startTime", startTime).queryParam("endTime", endTime)
                .queryParam("fromId", fromId).queryParam("limit", limit).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getFilters(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/filters", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String postPriceFilter(@NotEmpty Object maxPrice, @NotEmpty Object minPrice, @NotEmpty Object symbol,
            @NotEmpty Object tickSize) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/priceFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("minPrice", minPrice).queryParam("maxPrice", maxPrice)
                .queryParam("tickSize", tickSize).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deletePriceFilter(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/priceFilter", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postPositionFilter(@NotEmpty Object maxQty, @NotEmpty Object symbol, Object exemptAcct) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/positionFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("maxQty", maxQty).queryParam("exemptAcct", exemptAcct).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deletePositionFilter(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/positionFilter", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postLotSizeFilter(@NotEmpty Object maxQty, @NotEmpty Object minQty, @NotEmpty Object stepSize,
            @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/lotSizeFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("minQty", minQty).queryParam("stepSize", stepSize)
                .queryParam("maxQty", maxQty).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteLotSizeFilter(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/lotSizeFilter", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postTPlusSellFilter(@NotEmpty Object endTime, @NotEmpty Object symbol, Object exemptAcct) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/tPlusSellFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("endTime", endTime).queryParam("exemptAcct", exemptAcct)
                .build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteTPlusSellFilter(@NotEmpty Object symbol, Object force) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/tPlusSellFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("force", force).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postNumOrdersFilter(@NotEmpty Object numOrders, @NotEmpty Object symbol, Object exemptAcct) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/numOrdersFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("numOrders", numOrders).queryParam("exemptAcct", exemptAcct)
                .build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteNumOrdersFilter(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/numOrdersFilter", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postMinNotionalFilter(@NotEmpty Object enableMarket, @NotEmpty Object minNotional,
            @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/minNotionalFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("minNotional", minNotional)
                .queryParam("enableMarket", enableMarket).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteMinNotionalFilter(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/minNotionalFilter", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postNumAlgoOrdersFilter(@NotEmpty Object numOrders, @NotEmpty Object symbol, Object exemptAcct) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/numAlgoOrdersFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("numOrders", numOrders).queryParam("exemptAcct", exemptAcct)
                .build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteNumAlgoOrdersFilter(@NotEmpty Object numOrders, @NotEmpty Object symbol, Object exemptAcct) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/numAlgoOrdersFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("numOrders", numOrders).queryParam("exemptAcct", exemptAcct)
                .build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postIcebergPartsFilter(@NotEmpty Object limit, @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/icebergPartsFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("limit", limit).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteIcebergPartsFilter(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/icebergPartsFilter", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postMarketLotSizeFilter(@NotEmpty Object maxQty, @NotEmpty Object minQty, @NotEmpty Object stepSize,
            @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/marketLotSizeFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("minQty", minQty).queryParam("stepSize", stepSize)
                .queryParam("maxQty", maxQty).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteMarketLotSizeFilter(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/marketLotSizeFilter", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postPercentPriceFilter(@NotEmpty Object multiplierDown, @NotEmpty Object multiplierUp,
            @NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/percentPriceFilter", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("multiplierUp", multiplierUp)
                .queryParam("multiplierDown", multiplierDown).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deletePercentPriceFilter(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/percentPriceFilter", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String postNumIcebergOrders(@NotEmpty Object numOrders, @NotEmpty Object symbol, Object exemptAcct) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/numIcebergOrders", matchboxUrl))
                .queryParam("symbol", symbol).queryParam("numOrders", numOrders).queryParam("exemptAcct", exemptAcct)
                .build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String deleteNumIcebergOrders(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/numIcebergOrders", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String getCommission(@NotEmpty Object symbol) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/commission", matchboxUrl))
                .queryParam("symbol", symbol).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAssetBalance(@NotEmpty Object asset) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/assetBalance", matchboxUrl))
                .queryParam("asset", asset).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getAssetLedger(@NotEmpty Object asset, @NotEmpty Object endTime, @NotEmpty Object startTime) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/assetLedger", matchboxUrl))
                .queryParam("asset", asset).queryParam("startTime", startTime).queryParam("endTime", endTime).build()
                .toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String postUserDataStream(@NotEmpty Object accountId) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/userDataStream", matchboxUrl))
                .queryParam("accountId", accountId).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.postForObject(url, null, String.class);
    }

    @Override
    public String putUserDataStream(@NotEmpty Object listenKey) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/userDataStream", matchboxUrl))
                .queryParam("listenKey", listenKey).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String deleteUserDataStream(@NotEmpty Object listenKey) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/userDataStream", matchboxUrl))
                .queryParam("listenKey", listenKey).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.DELETE, null, String.class).getBody();
    }

    @Override
    public String putAllSymbols(@NotEmpty Object symbolStatus) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/allSymbols", matchboxUrl))
                .queryParam("symbolStatus", symbolStatus).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String putExchangeRate(@NotEmpty Object baseAsset, @NotEmpty Object price, @NotEmpty Object quoteAsset,
            Object invert) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/exchangeRate", matchboxUrl))
                .queryParam("baseAsset", baseAsset).queryParam("quoteAsset", quoteAsset).queryParam("price", price)
                .queryParam("invert", invert).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String putExchangeGas(@NotEmpty Object asset, @NotEmpty Object bips) {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/exchangeGas", matchboxUrl))
                .queryParam("asset", asset).queryParam("bips", bips).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.exchange(url, HttpMethod.PUT, null, String.class).getBody();
    }

    @Override
    public String getPing() {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/ping", matchboxUrl)).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getTime() {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/time", matchboxUrl)).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getCommands() {
        String url = UriComponentsBuilder.fromHttpUrl(String.format("%s/commands", matchboxUrl)).build().toUriString();
        url = formatUrl(url);
        return this.restTemplate.getForObject(url, String.class);
    }

}
